package com.unswift.cloud.core;

import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.codec.JsonJacksonCodec;
import org.redisson.config.ClusterServersConfig;
import org.redisson.config.Config;
import org.redisson.config.SingleServerConfig;
import org.springframework.amqp.core.AcknowledgeMode;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory.ConfirmType;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.unswift.annotation.api.Api;
import com.unswift.annotation.api.ApiField;
import com.unswift.annotation.api.ApiMethod;
import com.unswift.cache.MemoryCache;
import com.unswift.cloud.constant.ApplicationContant;
import com.unswift.utils.ExceptionUtils;
import com.unswift.utils.ObjectUtils;

@Configuration
@Api(value="应用手动加载Bean", author="unswift", date="2023-06-16", version="1.0.0")
public class ApplicationAutoBean {

	/********************************缓存*******************************/
	@Bean
	@ApiMethod(value="创建内存缓存Bean", returns=@ApiField("内存缓存Bean"))
	public MemoryCache getMemoryCache(){
		MemoryCache cache = MemoryCache.getInstance();
		ApplicationContant.loadExceptionMessage();
		return cache;
	}
	/********************************缓存*******************************/
	
	/********************************Rabbit Mq*******************************/
	@Bean(name = "connectionFactory")
	@ConditionalOnProperty(name="spring.rabbitmq.host")
	@ApiMethod(value="创建Rabbit Mq单节点连接工厂Bean", params={@ApiField("主机"), @ApiField("端口"), @ApiField("用户名"), @ApiField("密码"), @ApiField("确认消费"), @ApiField("主机"), @ApiField("确认回滚")}, returns=@ApiField("Rabbit Mq连接工厂"))
	public CachingConnectionFactory connectionFactorySingle(@Value("${spring.rabbitmq.host}") String host,
			@Value("${spring.rabbitmq.port}") int port,
			@Value("${spring.rabbitmq.username}") String username,
			@Value("${spring.rabbitmq.password}") String password,
			@Value("${spring.rabbitmq.virtual-host:/}") String virtualHost,
			@Value("${spring.rabbitmq.publisher-confirm-type}") String publisherConfirmType,
			@Value("${spring.rabbitmq.publisher-returns}") Boolean publisherReturns
			) {
		CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
		connectionFactory.setHost(host);
		connectionFactory.setPort(port);
		connectionFactory.setUsername(username);
		connectionFactory.setPassword(password);
		connectionFactory.setPublisherConfirmType("correlated".equals(publisherConfirmType)?ConfirmType.CORRELATED:"simple".equals(publisherConfirmType)?ConfirmType.SIMPLE:ConfirmType.NONE);
		connectionFactory.setPublisherReturns(publisherReturns);
		connectionFactory.setVirtualHost(virtualHost);
		return connectionFactory;
	}

	@Bean(name = "connectionFactory")
	@ConditionalOnProperty(name="spring.rabbitmq.addresses")
	@ApiMethod(value="创建Rabbit Mq集群连接工厂Bean", params={@ApiField("集群地址"), @ApiField("用户名"), @ApiField("密码"), @ApiField("确认消费"), @ApiField("主机"), @ApiField("确认回滚")}, returns=@ApiField("Rabbit Mq连接工厂"))
	public CachingConnectionFactory connectionFactoryCluster(@Value("${spring.rabbitmq.addresses}") String addresses,
			@Value("${spring.rabbitmq.username}") String username,
			@Value("${spring.rabbitmq.password}") String password,
			@Value("${spring.rabbitmq.publisher-confirm-type}") String publisherConfirmType,
			@Value("${spring.rabbitmq.virtual-host:/}") String virtualHost,
			@Value("${spring.rabbitmq.publisher-returns}") Boolean publisherReturns) {
		CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
		connectionFactory.setAddresses(addresses);
		connectionFactory.setUsername(username);
		connectionFactory.setPassword(password);
		connectionFactory.setPublisherConfirmType("correlated".equals(publisherConfirmType)?ConfirmType.CORRELATED:"simple".equals(publisherConfirmType)?ConfirmType.SIMPLE:ConfirmType.NONE);
		connectionFactory.setPublisherReturns(publisherReturns);
		connectionFactory.setVirtualHost(virtualHost);
		return connectionFactory;
	}

	@Bean(name = "rabbitTemplate")
	@ApiMethod(value="创建Rabbit Mq模板，使用此模板来发送消息", params=@ApiField("链接工厂"), returns=@ApiField("Rabbit Mq模板"))
	public RabbitTemplate rabbitTemplate(@Qualifier("connectionFactory") ConnectionFactory connectionFactory) {
		RabbitTemplate template = new RabbitTemplate(connectionFactory);
		template.setMessageConverter(new Jackson2JsonMessageConverter());
		return template;
	}

	@Bean(name = "rabbitListenerContainerFactory")
	@ApiMethod(value="创建Rabbit Mq监听工厂", params={@ApiField("链接工厂"), @ApiField("批量消费的条数")}, returns=@ApiField("监听工厂"))
	public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory(
			@Qualifier("connectionFactory") ConnectionFactory connectionFactory, 
			@Value("${spring.rabbitmq.batchSize:25}") Integer batchSize) {
		SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
		factory.setConnectionFactory(connectionFactory);
		factory.setMessageConverter(new Jackson2JsonMessageConverter());
		factory.setAcknowledgeMode(AcknowledgeMode.MANUAL);
		factory.setBatchSize(batchSize);
		return factory;
	}
	/********************************Rabbit Mq*******************************/
	
	/********************************Http Client*******************************/
	@Bean
	@ApiMethod(value="获取http请求客户端Bean", 
		params= {@ApiField("连接池最大数量"), 
				@ApiField("连接池默认路由"), 
				@ApiField("socket超时时间"), 
				@ApiField("连接超时时间"), 
				@ApiField("连接请求超时时间")}, 
		returns = @ApiField("http请求客户端"))
	public HttpClient getHttpClient(@Value("${http-client.pool.max-total}") int maxTotal,
			@Value("${http-client.pool.default-max-per-route}") int defaultMaxPerRoute,
			@Value("${http-client.connection.socket-timeout}") int socketTimeout,
			@Value("${http-client.connection.socket-timeout}") int connectTimeout,
			@Value("${http-client.connection.socket-timeout}") int connectionRequestTimeout
	){
		try {
			RequestConfig.Builder builder = RequestConfig.custom();
			builder.setSocketTimeout(socketTimeout)//3.1设置客户端等待服务端返回数据的超时时间
			.setConnectTimeout(connectTimeout)//3.2设置客户端发起TCP连接请求的超时时间
			.setConnectionRequestTimeout(connectionRequestTimeout);//3.3设置客户端从连接池获取链接的超时时间
			PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
			connectionManager.setMaxTotal(maxTotal);
			connectionManager.setDefaultMaxPerRoute(defaultMaxPerRoute);
			return HttpClients.custom().setConnectionManager(connectionManager).disableAutomaticRetries().setDefaultRequestConfig(builder.build()).build();
		} catch (Exception e) {
			e.printStackTrace();
			throw ExceptionUtils.exception("http.pool.init", e, e.getMessage());
		}
	}
	/********************************Http Client*******************************/
	
	/********************************Redisson*******************************/
	/**
     * 集群模式自动装配
     * @return
     */
	@Bean(name = "redissonClient")
	@ConditionalOnProperty(name = "spring.redisson.node-addresses")
	@ApiMethod(value="redis集群模式bean", params = {
		@ApiField("连接地址"),
		@ApiField("扫描间隔"),
		@ApiField("超时时间"),
		@ApiField("最大等待超时时间"),
		@ApiField("最小连接池数量"),
		@ApiField("最大连接池数量"),
		@ApiField("ping连接间隔"),
		@ApiField("连接密码")
	}, returns = @ApiField("集群redis对象"))
	public RedissonClient redissonSentinel(@Value("${spring.redisson.node-addresses}") String nodeAddresses,
			@Value("${spring.redisson.scan-interval") int scanInterval, @Value("spring.redisson.timeout}") int timeout,
			@Value("${spring.redisson.lettuce.pool.max-wait}") int idleConnectionTimeout,
			@Value("${spring.redisson.lettuce.pool.min-idle}") int minIdle,
			@Value("${spring.redisson.lettuce.pool.max-idle}") int maxIdle,
			@Value("${spring.redisson.ping-connection}") int pingConnection,
			@Value("${spring.redisson.password:}") String password) {
		Config config = new Config();
		config.setNettyThreads(2);
		ClusterServersConfig serverConfig = config.useClusterServers().addNodeAddress(nodeAddresses)
				.setScanInterval(scanInterval).setConnectTimeout(timeout)
				.setIdleConnectionTimeout(idleConnectionTimeout).setTimeout(timeout)
				.setMasterConnectionPoolSize(maxIdle).setSlaveConnectionPoolSize(maxIdle)
				.setMasterConnectionMinimumIdleSize(minIdle).setPingConnectionInterval(pingConnection)
				.setSlaveConnectionMinimumIdleSize(minIdle);

		if (ObjectUtils.isNotEmpty(password)) {
			serverConfig.setPassword(password);
		}
		config.setCodec(new JsonJacksonCodec());
		return Redisson.create(config);
	}

	/**
	 * 单机模式自动装配
	 * 
	 * @return
	 */
	@Bean(name = "redissonClient")
	@ConditionalOnProperty(name = "spring.redisson.host")
	@ApiMethod(value="redis单例模式bean", params = {
			@ApiField("连接主机"),
			@ApiField("连接端口"),
			@ApiField("超时时间"),
			@ApiField("最大等待超时时间"),
			@ApiField("最小连接池数量"),
			@ApiField("最大连接池数量"),
			@ApiField("ping连接间隔"),
			@ApiField("连接密码"),
			@ApiField("使用的数据库")
		}, returns = @ApiField("单例redis对象"))
	public RedissonClient redissonSingle(@Value("${spring.redisson.host}") String host,
			@Value("${spring.redisson.port}") String port,
			@Value("${spring.redisson.timeout}") int timeout,
			@Value("${spring.redisson.lettuce.pool.max-wait}") int idleConnectionTimeout,
			@Value("${spring.redisson.lettuce.pool.min-idle}") int minIdle,
			@Value("${spring.redisson.lettuce.pool.max-idle}") int maxIdle,
			@Value("${spring.redisson.ping-connection}") int pingConnection,
			@Value("${spring.redisson.password:}") String password, @Value("${spring.redisson.database:0}") int database) {
		Config config = new Config();
		config.setNettyThreads(2);
		SingleServerConfig serverConfig = config.useSingleServer().setAddress("redis://" + host + ":" + port)
				.setConnectTimeout(timeout).setIdleConnectionTimeout(idleConnectionTimeout).setTimeout(timeout)
				.setConnectionPoolSize(maxIdle).setPingConnectionInterval(pingConnection)// **此项务必设置为redisson解决之前bug的timeout问题关键*****
				.setConnectionMinimumIdleSize(minIdle);

		if (ObjectUtils.isNotEmpty(password)) {
			serverConfig.setPassword(password);
		}
		if (ObjectUtils.isNotEmpty(database)) {
			serverConfig.setDatabase(database);
		}
		config.setCodec(new JsonJacksonCodec());
		return Redisson.create(config);
	}
	/********************************Redisson*******************************/
}
